﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

namespace CodeAngel.Domain.Framework
{
    #region IEntityCollectionProperty

    /// <summary>
    /// Interface for a property that contains a collection of entities.
    /// </summary>
    public interface IEntityCollectionProperty : IProperty
    {
        /// <summary>
        /// Indicates that an entity has been added to this collection property.
        /// </summary>
        event EntityAddedEventHandler EntityAdded;

        /// <summary>
        /// Indicates that an entity has been removed from this collection property.
        /// </summary>
        event EntityRemovedEventHandler EntityRemoved;

        /// <summary>
        /// Gets the value of this property.
        /// </summary>
        IEntityCollection GetValue();
    }

    #endregion

    #region EntityCollectionProperty<TEntityCollection, TEntity>

    /// <summary>
    /// Base class for strongly-typed entity collection properties.
    /// </summary>
    public class EntityCollectionProperty<TEntityCollection, TEntity> : IEntityCollectionProperty
        where TEntityCollection : EntityCollection<TEntity>
        where TEntity : Entity
    {

        #region Constructors

        /// <summary>
        /// Creates a new collection property.
        /// </summary>
        public EntityCollectionProperty(string name, string friendlyName, Entity parentEntity, TEntityCollection value)
        {
            this.Name = name;
            this.FriendlyName = friendlyName;
            this.ParentEntity = parentEntity;
            this.ValidationRules = new RuleCollection();
            this.Value = value;
            this.Value.CollectionChanged += Value_CollectionChanged;
        }

        #endregion

        /// <summary>
        /// Identifies the name of this property.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Identifies the name of this property that is appropriate to display to the user.
        /// </summary>
        public string FriendlyName { get; private set; }

        /// <summary>
        /// Identifies the parent entity that this property belongs to.
        /// </summary>
        public Entity ParentEntity { get; private set; }

        /// <summary>
        /// Identifies the value of this property.
        /// </summary>
        public TEntityCollection Value { get; private set; }

        #region Value_CollectionChanged

        /// <summary>
        /// Handles when an entity is added or removed from our entity collection and provides notification
        /// that a change has occurred through our EntityAdded/EntityRemoved events.
        /// </summary>
        private void Value_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 0)
            {
                var ThisEntity = e.NewItems[0] as Entity;
                this.RaiseEntityAdded(ThisEntity);
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 0)
            {
                var ThisEntity = e.OldItems[0] as Entity;
                this.RaiseEntityRemoved(ThisEntity);
            }
        }

        #endregion

        #region EntityRemoved & EntityAdded Events

        /// <summary>
        /// Indicates that an entity has been added to our entity collection
        /// </summary>
        public event EntityAddedEventHandler EntityAdded;
        protected void RaiseEntityAdded(Entity thisEntity)
        {
            if (thisEntity != null && this.EntityAdded != null) this.EntityAdded(this, new EntityEventArgs(thisEntity));
        }

        /// <summary>
        /// Indicates that an entity has been removed from our entity collection
        /// </summary>
        public event EntityRemovedEventHandler EntityRemoved;
        protected void RaiseEntityRemoved(Entity thisEntity)
        {
            if (thisEntity != null && this.EntityRemoved != null) this.EntityRemoved(this, new EntityEventArgs(thisEntity));
        }

        #endregion

        #region Validation

        /// <summary>
        /// Identifies the business rules of this property that relate to whether our parent 
        /// entity should be allowed to be saved.
        /// </summary>
        public RuleCollection ValidationRules { get; private set; }

        /// <summary>
        /// Identifies if all rules associated with this property are valid, including each entity
        /// that may be within the collection of this property.
        /// </summary>
        public bool IsValid()
        {
            // First, check rules at the property level.
            if (!this.ValidationRules.IsValid()) return false;

            // Next, if we have a collection of entities, then verify each entity is valid.
            if (this.Value != null)
            {
                foreach (var ChildEntity in this.Value)
                {

                    if (!ChildEntity.IsValid()) return false;
                }
            }

            return true;

        }

        /// <summary>
        /// Identifies any rules assigned to this property that are not valid.
        /// </summary>
        public RuleCollection GetInvalidRules()
        {
            return this.ValidationRules.GetInvalidRules();
        }

        #endregion

        #region GetValue

        /// <summary>
        /// Gets the value of this property as an IEntityCollection.
        /// </summary>
        public IEntityCollection GetValue()
        {
            return this.Value as IEntityCollection;
        }

        #endregion

    }

    #endregion

    #region EntityAddedEventHandler

    public delegate void EntityAddedEventHandler(object sender, EntityEventArgs e);

    #endregion

    #region EntityRemovedEventHandler

    public delegate void EntityRemovedEventHandler(object sender, EntityEventArgs e);

    #endregion

    #region EntityEventArgs

    /// <summary>
    /// Event args that identifies a specific entity object.
    /// </summary>
    public class EntityEventArgs : EventArgs
    {

        internal EntityEventArgs(Entity thisEntity)
        {
            this.Entity = thisEntity;
        }

        public Entity Entity { get; private set; }

    }

    #endregion
}
